Skip to content

fix(auth): exempt scaffold_classify from scope enforcement (#51)#52

Merged
stackbilt-admin merged 1 commit into
mainfrom
fix/scaffold-classify-401
Jun 12, 2026
Merged

fix(auth): exempt scaffold_classify from scope enforcement (#51)#52
stackbilt-admin merged 1 commit into
mainfrom
fix/scaffold-classify-401

Conversation

@stackbilt-admin

Copy link
Copy Markdown
Member

What broke

scaffold_classify was returning a scope enforcement error (surfaced as 401 by some MCP clients) for any bearer token that lacked 'read' or 'generate' scope — including:

Root cause

RISK_REQUIRED_SCOPES['READ_ONLY'] = ['read', 'generate']. Every READ_ONLY tool, including scaffold_classify, was blocked for scopes: [] tokens. The C-1b remediation was correct for mutation tools but too aggressive for zero-cost, side-effect-free operations.

Fix

Introduce SCOPE_EXEMPT_TOOLS — a set of tools callable by any valid authenticated bearer token, regardless of scope grant. Criteria: baseCost=0, no external calls, no state mutation, pure read/heuristic.

Tools added to the exempt set:

  • scaffold_classify — keyword matching against the aegis-intents deck, no LLM, no quota
  • scaffold_status — health/version probe
  • image_list_models — static model catalog

image_check_job is NOT exempt — it reads tenant-scoped job state and should keep requiring 'read' scope.

Unauthenticated requests are still rejected at the auth layer before scope enforcement is reached — this does not weaken security, it removes friction for a tool that has nothing to protect.

Tests

  • Updated gateway.test.ts: fixed "denies all tool calls when token has no scopes" to use scaffold_create (LOCAL_MUTATION); added two new scaffold_classify scope-exempt assertions (no scopes, read-only scope)
  • Updated gateway-legacy-scope.test.ts: same fix — swapped scaffold_status for scaffold_create in the empty-scope blocking test

All 185 tests pass.

🤖 Generated with Claude Code

scaffold_classify is zero-cost heuristic keyword matching — no LLM call,
no backend state mutation, no quota consumption. Requiring 'generate'
(or even 'read') scope was blocking it for tokens minted without explicit
scope (e.g. pre-#28 OAuth clients, API keys from older edge-auth versions).

Root cause: RISK_REQUIRED_SCOPES['READ_ONLY'] = ['read', 'generate'],
so any READ_ONLY tool silently 401s/403s for tokens with scopes:[].
scaffold_classify, scaffold_status, and image_list_models are the only
tools where this is wrong — they carry no privilege worth protecting.

Fix: introduce SCOPE_EXEMPT_TOOLS set. Tools in this set skip the
RISK_REQUIRED_SCOPES check entirely. Any valid authenticated bearer
token can call them; unauthenticated requests are still rejected at the
auth layer before we reach scope enforcement.

Also updates two tests that were asserting the wrong tool name (they used
scaffold_status, which is now also exempt, to verify scope blocking; both
now use scaffold_create which is LOCAL_MUTATION and correctly requires scope).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@stackbilt-admin stackbilt-admin merged commit a671fe1 into main Jun 12, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant